package com.lagou.factory;

import com.lagou.annotion.MyAutowired;
import com.lagou.annotion.MyService;
import com.lagou.annotion.MyTransactional;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @Author ZHAO Yudong
 * @Date 2020/11/6 14:37
 * @description：
 */
@Slf4j
public class BeanFactory {


	// 存储对象
	private static Map<String, Object> map = new HashMap<>();

	static {
		try {
			//扫描包，分析需实例化的对象列表（service和事务管理器）,通过反射技术实例化对象并且存储待用（map集合）
			//扫描获取反射对象集合
			Reflections f = new Reflections("com.lagou");
			//获取注解为service的集合
			Set<Class<?>> set = f.getTypesAnnotatedWith(MyService.class);
			for (Class<?> c : set) {
				// 通过反射技术实例化对象
				Object bean = c.newInstance();
				log.info("service = {}", c);
				MyService annotation = c.getAnnotation(MyService.class);

				//对象ID在service注解有value时用value，没有时用类名
				if (StringUtils.isEmpty(annotation.value())) {
					//由于getName获取的是全限定类名，所以要分割去掉前面包名部分
					String[] names = c.getName().split("\\.");
					map.put(names[names.length - 1], bean);
				} else {
					map.put(annotation.value(), bean);
				}
			}
			// 实例化完成之后维护对象的依赖关系，检查哪些对象需要传值进入，根据它的配置，我们传入相应的值
			for (Map.Entry<String, Object> a : map.entrySet()) {
				Object o = a.getValue();
				Class c = o.getClass();
				log.info("class = {}", c);
				//获取属性集合
				Field[] fields = c.getDeclaredFields();
				//遍历属性，若持有Autowired注解则注入
				for (Field field : fields) {
					if (field.isAnnotationPresent(MyAutowired.class)
						&& field.getAnnotation(MyAutowired.class).value()) {
						String[] names = field.getType().getName().split("\\.");
						String name = names[names.length - 1];
						Method[] methods = c.getMethods();
						for (int j = 0; j < methods.length; j++) {
							Method method = methods[j];
							// 该方法就是 setAccountDao(AccountDao accountDao)
							if (method.getName().equalsIgnoreCase("set" + name)) {
								method.invoke(o, map.get(name));
							}
						}
					}
				}
				//判断对象类是否持有Transactional注解，若有则修改对象为代理对象
				if (c.isAnnotationPresent(MyTransactional.class)) {
					//获取代理工厂
					ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
					//判断对象是否实现接口
					//获取类c实现的所有接口
					Class[] face = c.getInterfaces();
					if (face != null && face.length > 0) {
						//实现使用JDK
						o = proxyFactory.getJdkProxy(o);
					} else {
						//没实现使用CGLIB
						o = proxyFactory.getCglibProxy(o);
					}
				}
				// 把处理之后的object重新放到map中
				map.put(a.getKey(), o);
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	public static Object getBean(String id) {
		return map.get(id);
	}

}
